package system

import (
	"context"
	"encoding/json"
	"fmt"
	redis2 "github.com/go-redis/redis/v8"
	"github.com/google/uuid"
	"go.uber.org/zap"
	"jgyApi/server/global"
	systemReq "jgyApi/server/model/system/request"
	"strconv"
	"sync"
	"time"
)

//var (
//	JobPoolKey         = "job_pool_key_"
//	BaseDelayBucketKey = "base_delay_bucket"
//	BaseReadyQueueKey  = "base_ready_queue"
//	delayAddTime       = 864000
//)
type DelayInfo struct {
	JobPoolKey string
	BaseDelayBucketKey string
	BaseReadyQueueKey string
	DelayAddTime int
}

type RedisJobData struct {
	Topic string
	ID    string
	Delay int
	TTR   int
	//Body  interface{}  todo 现在还知道怎样改为继承的关系
	Body systemReq.QidActive
}

func (d *RedisJobData) MarshalBinary() (data []byte, err error) {
	return json.Marshal(d)
}

func (d *RedisJobData) UnmarshalBinary(data []byte) (err error) {
	return json.Unmarshal(data, d)
}


//type BodyContent struct {
//	OrderID   int
//	OrderName string
//}

//组装数据
func (d *RedisJobData) SetJobPool(number int, info DelayInfo, ctx context.Context) bool {
	redisCoon := global.GVA_REDIS
	//r := rand.New(rand.NewSource(time.Now().Unix()))
	for i := 0; i < number; i++ {
		d.Topic = "order_queue"
		d.ID = uuid.NewString()
		d.Delay = 1
		d.TTR = 3
		key := info.JobPoolKey + d.Body.Qid
		delayKey := d.Body.Qid
		//写入job pool
		_, err := redisCoon.Set(ctx, key, d, 0*time.Second).Result()
		if err != nil {
			fmt.Println("添加失败: ", err)
			return false
		}
		fmt.Println("添加成功: ", err)
		nowTime := time.Now().Unix()

		delayTime := int(nowTime) + info.DelayAddTime
		//写入delay queue
		redisCoon.ZAdd(ctx, info.BaseDelayBucketKey, &redis2.Z{
			Score:  float64(delayTime),
			Member: delayKey,
		})
		//为了可以更好的演示，这每个过期时间增加几秒，防止一次性消费了
		//if i%10 == 0 && i > 0 {
		//	delayAddTime += 10
		//}
	}
	return true
}

//定时timer.tick查询bucket中是否有过期的数据，如果有放入消费队列中
func TimerDelayBucket(redisCoon *redis2.Client, ctx context.Context, pool *Pool, wg *sync.WaitGroup, info DelayInfo) error {
	nowTime := time.Now().Unix()
	result, err := redisCoon.ZRangeByScoreWithScores(ctx, info.BaseDelayBucketKey, &redis2.ZRangeBy{
		Min: "-inf",
		Max: strconv.FormatInt(nowTime, 10),
	}).Result()
	if err == nil {
		for _, z := range result {
			//进入ready queue
			redisCoon.LPush(ctx, info.BaseReadyQueueKey, z.Member)
			//写入通道说明有数据了，可以进行消费
			err := pool.Put(&Task{
				Member: z.Member.(string),
				Wg:     wg,
			}, info)
			if err != nil {
				fmt.Println(err)
			}
			wg.Add(1)
		}
	}
	return err

}

//消费队列
func ConsumeQueue(redisCoon *redis2.Client, ctx context.Context, wg *sync.WaitGroup, info DelayInfo) error {
	defer wg.Done()
	//先判断list中是否有数据，有数据才有必要执行，没有数据直接返回就好了
	lenQueue, err := redisCoon.LLen(ctx, info.BaseReadyQueueKey).Result()
	if err != nil {
		return err
	}
	if lenQueue == 0 {
		return nil
	}
	result, err := redisCoon.LPop(ctx, info.BaseReadyQueueKey).Result()
	if err != nil {
		return err
	}
	fmt.Println("我消费了一个数据：", result)
	//这里可以实现需要的操作，这里简单实现了删除操作
	jobData := RedisJobData{}
	err = global.GVA_REDIS.Get(context.Background(), info.JobPoolKey+result).Scan(&jobData)
	if err != nil {
		global.GVA_LOG.Error(err.Error())
	}

	//到期自动激活赶考账号
	_, err = ActiveGanKaoUser(&jobData.Body)
	if err != nil {
		global.GVA_LOG.Error(err.Error(), zap.Any("body", &jobData.Body))
		redisCoon.Del(ctx, info.JobPoolKey+result)
		redisCoon.ZRem(ctx, info.BaseDelayBucketKey, result)
		return nil
	}
	redisCoon.Del(ctx, info.JobPoolKey+result)
	redisCoon.ZRem(ctx, info.BaseDelayBucketKey, result)
	return nil
}
